/** 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package it.free.i.virtual.machine.hal.devicealias.laboratory.simulator.dynamicparameter;

import it.free.i.naming.CachedNamingProviderProxy;
import it.free.i.naming.NamingBuilder;
import it.free.i.virtual.machine.hal.devicealias.DeviceAliasFactory;
import it.free.i.virtual.machine.hal.devicealias.laboratory.SimulationLaboratoryGeneralException;
import it.free.i.virtual.machine.hal.devicealias.laboratory.simulator.dynamicparameter.impl.DynamicParameterDecreasingImpl;

import java.util.Observable;
import java.util.Properties;

import org.apache.log4j.Logger;

/**
 * Factory for implementations of the dynamic parameter
 * @author stefano
 */
public class DynamicParameterFactory {

	public static String CONTEXT = null;
	private static final String MY_CONTEXT = "cn=DynamicParameters";
	private static Logger logger = null;
	
	static{
		logger = Logger.getLogger(DynamicParameterFactory.class);
		CONTEXT = NamingBuilder.buildLookupName(MY_CONTEXT, DeviceAliasFactory.CONTEXT);
	}
	
	/**
	 * Non accessible class builder
	 */
	private DynamicParameterFactory(){	
	}
	
	/**
	 * Object factory to support the creation of dynamic parameter implementations 
	 * @param algorithm can be one of those foreseen in <code>it.free.i.virtual.machine.hal.devicealias.laboratory.simulator.dynamicparameter.DynamicParameterAlgorithms</code>
	 * @param numberOfBeats is both the threshold and the number of earth beat to performe to change the value up to the threshold
	 * @param hearBeatYeldPause is the pause in milliseconds between two earth beats. This define how fast the parameter change in its thread  
	 * @param parameterName is the name of this parameter: for example "fuel", "engine oil", "battery charge" and so on
	 * @param dynamicParameterEventsObserver can be null, as most of the time it is expected to be,
	 *        if no one is interested in events generated by the instance created by this factory
	   @param reactOnCollisionWithObject is the type of object collision with, implies a reaction like increasing
	   		  increasing a parameter automatically decreasing
	 * @return the instance of the dynamic parameter implementing the passed algorithm 
	 * @throws SimulationLaboratoryGeneralException
	 */
	private static final DynamicParameter createDynamicParameter(DynamicParameterAlgorithms algorithm, 
										 			  			long numberOfBeats,
										 			  			long hearBeatYeldPause,
										 			  			String parameterName,
										 			  			String reactOnCollisionWithObject) throws SimulationLaboratoryGeneralException {
		
		if((algorithm == null) || 
		   (!algorithm.toString().equalsIgnoreCase(DynamicParameterAlgorithms.DECREASING.toString())) ){
	       throw (new SimulationLaboratoryGeneralException("Unknown dynamic parameter fluctuation algorithm: [" + algorithm + "]"));
		}
		
		DynamicParameter dynamicParameter = new DynamicParameterDecreasingImpl();
		dynamicParameter.setName(parameterName);
		dynamicParameter.setHeartBeat(numberOfBeats, hearBeatYeldPause);
		dynamicParameter.setReactOnCollisionWithObject(reactOnCollisionWithObject);
		
		return dynamicParameter;
	}
	
	/**
	 * Create via a jndi name a new dynamic parameter registering it
	 * @param lookupName is the name to look up on the jndi repository to find configurations
	 * @param agentThatCauseParameterToChange is the observable causing this parameter to change, CAN be null 
	 * @return a configured and registered instance of dynamic parameter
	 * @throws Exception
	 */
	public static Object createObject(String lookupName,
							   		  Observable agentThatCauseParameterToChange) throws Exception {

		String compositeLookupName = NamingBuilder.buildLookupName("cn=" + lookupName, DynamicParameterFactory.CONTEXT);
		
		logger.info("Looking up dynamic parameter configuration for jndi name: [" + compositeLookupName + "]");
		
		Properties parameterConfiguration = CachedNamingProviderProxy.getInstance().lookUpConfiguration(compositeLookupName);
				
		if( (parameterConfiguration == null) ||
			 parameterConfiguration.isEmpty() ){
			throw (new Exception("Unable to find dynamic parameter configuration for lookup name: [" + lookupName + "]"));
		}
		
		DynamicParameter dynamicParameterImpl = null;
		
		long heartBeatYeldPause = Long.parseLong(parameterConfiguration.getProperty(DynamicParameter.HEARTBEAT_YELD_PAUSE));
		long heartBeatsNumber = Long.parseLong(parameterConfiguration.getProperty(DynamicParameter.HEARTBEATS_NUMBER));
		String reactOnCollisionWithObject = parameterConfiguration.getProperty(DynamicParameter.REACT_ON_COLLISION_WITH_OBJECT);
		
		//Create an instance of a parameter via the standard factory: the Observer "this" is the object that will receive
		// notifications about the parameter
		dynamicParameterImpl = DynamicParameterFactory.createDynamicParameter(DynamicParameterAlgorithms.DECREASING, 
							   											      heartBeatsNumber,
																              heartBeatYeldPause, 
																              lookupName,
																              reactOnCollisionWithObject);

		dynamicParameterImpl.setReactOnCollisionWithObject(reactOnCollisionWithObject);
		
		//Register the parameter created via factory associating it with a name
		DynamicParameterRegistry.getInstance().setParameter(compositeLookupName,
															dynamicParameterImpl);

		//Says to the parameter instance created the generator of events that cause this parameter to change
		DynamicParameterRegistry.getInstance().registerOnParameter(compositeLookupName, 
		   													       agentThatCauseParameterToChange);

		//Cause this parameter worker to be run as a thread in its own parallel process.
		// This will allow the parameter to change in an independent process
		DynamicParameterRegistry.getInstance().daemonizeParameter(compositeLookupName);

		return dynamicParameterImpl;
	
	}
	
}
